function utf8Encode(argString) {
    // http://kevin.vanzonneveld.net
    // +   original by: Webtoolkit.info (http://www.webtoolkit.info/)
    // +   improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
    // +   improved by: sowberry
    // +    tweaked by: Jack
    // +   bugfixed by: Onno Marsman
    // +   improved by: Yves Sucaet
    // +   bugfixed by: Onno Marsman
    // +   bugfixed by: Ulrich
    // +   bugfixed by: Rafal Kukawski
    // +   improved by: kirilloid
    // +   bugfixed by: kirilloid
    // *     example 1: this.utf8Encode('Kevin van Zonneveld')
    // *     returns 1: 'Kevin van Zonneveld'

    if (argString === null || typeof argString === 'undefined') {
        return ''
    }

    let string = argString + '' // .replace(/\r\n/g, '\n').replace(/\r/g, '\n')
    let utftext = '',
        start,
        end,
        stringl = 0

    start = end = 0
    stringl = string.length
    for (let n = 0; n < stringl; n++) {
        let c1 = string.charCodeAt(n)
        let enc = null

        if (c1 < 128) {
            end++
        } else if (c1 > 127 && c1 < 2048) {
            enc = String.fromCharCode((c1 >> 6) | 192, (c1 & 63) | 128)
        } else if (((c1 & 0xf800) ^ 0xd800) > 0) {
            enc = String.fromCharCode(
                (c1 >> 12) | 224,
                ((c1 >> 6) & 63) | 128,
                (c1 & 63) | 128
            )
        } else {
            // surrogate pairs
            if (((c1 & 0xfc00) ^ 0xd800) > 0) {
                throw new RangeError('Unmatched trail surrogate at ' + n)
            }
            let c2 = string.charCodeAt(++n)
            if (((c2 & 0xfc00) ^ 0xdc00) > 0) {
                throw new RangeError('Unmatched lead surrogate at ' + (n - 1))
            }
            c1 = ((c1 & 0x3ff) << 10) + (c2 & 0x3ff) + 0x10000
            enc = String.fromCharCode(
                (c1 >> 18) | 240,
                ((c1 >> 12) & 63) | 128,
                ((c1 >> 6) & 63) | 128,
                (c1 & 63) | 128
            )
        }
        if (enc !== null) {
            if (end > start) {
                utftext += string.slice(start, end)
            }
            utftext += enc
            start = end = n + 1
        }
    }

    if (end > start) {
        utftext += string.slice(start, stringl)
    }

    return utftext
}

function base64Encode(data) {
    // http://kevin.vanzonneveld.net
    // +   original by: Tyler Akins (http://rumkin.com)
    // +   improved by: Bayron Guevara
    // +   improved by: Thunder.m
    // +   improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
    // +   bugfixed by: Pellentesque Malesuada
    // +   improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
    // -    depends on: this.utf8Encode
    // *     example 1: this.base64Encode('Kevin van Zonneveld')
    // *     returns 1: 'S2V2aW4gdmFuIFpvbm5ldmVsZA=='
    // mozilla has this native
    // - but breaks in 2.0.0.12!
    // if (typeof this.window['atob'] == 'function') {
    //    return atob(data)
    // }
    let b64 = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/='
    let o1,
        o2,
        o3,
        h1,
        h2,
        h3,
        h4,
        bits,
        i = 0,
        ac = 0,
        enc = '',
        tmp_arr = []

    if (!data) {
        return data
    }

    data = utf8Encode(data + '')

    do {
        // pack three octets into four hexets
        o1 = data.charCodeAt(i++)
        o2 = data.charCodeAt(i++)
        o3 = data.charCodeAt(i++)

        bits = (o1 << 16) | (o2 << 8) | o3

        h1 = (bits >> 18) & 0x3f
        h2 = (bits >> 12) & 0x3f
        h3 = (bits >> 6) & 0x3f
        h4 = bits & 0x3f

        // use hexets to index into b64, and append result to encoded string
        tmp_arr[ac++] = b64.charAt(h1) + b64.charAt(h2) + b64.charAt(h3) + b64.charAt(h4)
    } while (i < data.length)

    enc = tmp_arr.join('')

    switch (data.length % 3) {
        case 1:
            enc = enc.slice(0, -2) + '=='
            break
        case 2:
            enc = enc.slice(0, -1) + '='
            break
    }

    return enc
}
export function urlSafeBase64Encode(v) {
    v = base64Encode(v)

    // 参考 https://tools.ietf.org/html/rfc4648#section-5
    return v.replace(/\//g, '_').replace(/\+/g, '-')
}
